Skip to content

fix: prevent disabled models from starting new training rounds - #305

Merged
robertocarlous merged 15 commits into
InfiniteZeroFoundation:developfrom
Santiagocetran:fix/model-disable-224
Nov 7, 2026
Merged

robertocarlous merged 15 commits into
InfiniteZeroFoundation:developfrom
Santiagocetran:fix/model-disable-224

Conversation

@Santiagocetran

Copy link
Copy Markdown
Collaborator

Addresses issue #224 based on the decisions in this discussion.

  • Both startGI overloads reject unregistered or disabled models, including when their reward pools are already funded.
  • Documents registration fees as non-refundable anti-spam costs.
  • Documents S6 slashing as parked for testnet, leaving its behavior and storage unchanged.

The auditor checks the registry-assigned model ID through immutable registry wiring. CLI constructor support and bundled ABIs are updated. In-flight rounds can still complete, settle and pay rewards.

Protection requires a newly deployed compatible coordinator/auditor pair wired to the canonical registry. Existing deployments are unchanged. No GI-abort or pool-refund mechanism is added.

Validation:

  • Clean full Foundry suite: 524 passed.
  • Selected Python tests: 29 passed.
  • Contract-size gate passed.
  • Bundled ABIs match build artifacts; all 293 documentation links resolve.
  • Full Docker/Kubo CLI integration was not run.

PR #261 should land first. The deployment CLI changes overlap, so this branch will need reconciliation after #261 lands. I’ll update it and rerun the relevant checks then.

@umeradl

umeradl commented Oct 30, 2026

Copy link
Copy Markdown
Member

Reviewed against develop in an isolated worktree (branch applies cleanly on top of current tip e9424af, which is also the merge-base; 1 commit, no conflicts — confirmed by both git merge-tree --write-tree locally and GitHub's own mergeable: MERGEABLE / mergeStateStatus: BLOCKED, where BLOCKED is only the missing approvals).

CI on 2a9c546: CI OK green — Solidity (full forge test plus the size gate), Python and Docs. The non-required Integration (full GI) job failed at test_01_platform.py::test_deploy_platform_via_script (missing proxyAdmin in foundry/deployments/localhost.json). That's the same assertion it fails on develop itself (push run on e9424af), so it isn't caused by this PR. It does mean CI never reached this PR's deploy CLI, so I reproduced that locally on anvil. Also reproduced locally: a break-then-fix of the new kill-switch tests, the contract sizes, and the bundled-ABI parity.

Claimed: both startGI overloads reject unregistered or disabled models, including with a funded pool, before any mutation.

Verified — correct, and the tests are load-bearing: _startGI checks state and _GI == GI + 1, then calls dinTaskAuditorContract.validateGIStart(_GI) before updatePassScore, GI++ and _setGIstate. The interface declares it view, so it's a STATICCALL and the auditor can't re-enter. The auditor reverse-looks-up its own address in the registry (TA_ModelNotRegistered), checks modelDisabled(registeredId) (TA_ModelDisabled), then the pool (TC_GIRewardPoolNotFunded, same selector as before). Break-then-fix: the 8 new RewardEngine tests all pass on the PR. With the coordinator line swapped back to develop's if (dinTaskAuditorContract.giRewardPool(_GI) == 0) revert TC_GIRewardPoolNotFunded();, 5 fail ("next call did not revert as expected"): disabled-both-overloads, private-funding-after-disable, unregistered, registry-read-failure, and disabled-in-flight. The 3 that still pass test the auditor directly (constructor wiring, onlyTaskCoordinator, wrong-registry). Restored the file afterwards, and the tree is clean.

Claimed: in-flight rounds can still complete, settle and pay rewards.

Verified — correct: test_disabledInFlight_completesSettlesClaims_butCannotStartNext disables during GI 1, runs it to endGI, claimReward and claimRewards (the balance rises by claimable), then shows both overloads revert for GI 2 with a funded pool. The guard appears only in _startGI; no other entry point changed.

Claimed: the auditor checks the registry-assigned ID, not the constructor's anticipated one.

Verified — correct: the fixture deliberately deploys with constructor ID 1 while the registry assigns ID 0. The test asserts getModelIdByTaskAuditor(ta) == (true, 0), ta.modelId() == 1, and TA_ModelDisabled(0).

Claimed: contract-size gate passes.

Verified — locally: forge build --sizes: DINTaskCoordinator runtime 23,387 B (1,189 B margin), and DINTaskAuditor 21,885 B. Putting the registry reads in the auditor, as Abraham suggested, keeps the coordinator nearly flat. The Solidity CI job enforces the gate too.

Claimed: bundled ABIs match the build artifacts.

Verified — exact: comparing dincli/abis/DINTask{Auditor,Coordinator}.json with foundry/out/… as sorted canonical JSON gives equal ABIs (160/160 and 201/201 entries). The coordinator ABI loses TC_GIRewardPoolNotFunded because that error is now raised by the auditor. dincli prints raw revert text and decodes no custom errors, so nothing in the CLI depends on it.

Claimed: the CLI deploy supports the new constructor shapes.

Verified — on a live anvil, with the real Foundry artifacts: I drove deploy.task_coordinator and deploy.task_auditor themselves. Only the stake/registry lookup, .env writes and sleep were stubbed; transactions went to anvil for real.

Case Result
coordinator, no --model-id BadParameter: This artifact requires --model-id… (no tx)
coordinator --model-id 7 deployed, modelId() == 7
auditor --model-id 8 vs coordinator 7 BadParameter: --model-id must match the paired coordinator modelId() (no tx)
auditor --model-id 7 deployed; modelRegistry() = din_info registry, modelId() == 7, and the coordinator's dinTaskAuditorContract = auditor (setDINTaskAuditorContract ran)
no registry in din_info BadParameter: Configure the canonical platform registry… (no tx)
--taskCoordinator pointing at a contract without modelId() raw ContractLogicError: execution reverted — see observation 2

tests/test_model_disable_deployment.py: 13 passed locally, and green in CI.

Storage / upgrade safety: both task contracts are non-upgradeable. modelRegistry is immutable (bytecode, not storage), and DINModelRegistry.sol changes only a NatSpec comment, so there is no storage-layout impact on the upgradeable proxy. DINShared.sol adds an interface and two errors only.

Docs: the decisions are recorded with a link to Abraham's comment, which I checked is the real comment ID. The 9 doc files consistently say "future GIs only, no abort, redeploy required, review wiring". Links are green in CI Docs.

Observations:

  1. The registry wiring is self-attested (the most important point here, but not a blocker). The only constructor check is modelRegistry_.code.length != 0, and DINModelRegistry.approveModel never reads the auditor's modelRegistry(). A model owner who deploys directly instead of through dincli can pass any contract that returns "registered, enabled". The EnabledModelRegistry test stub is exactly such a contract, and the live deploy above accepted it. That silently defeats the kill switch. The docs say this openly ("Immutable wiring … does not prove the supplied registry is canonical. The DIN-Representative must review…"), and arbitrary custom task code is out of scope anyway. Still, this one check is cheap to make mechanical, in either of two ways:

    • dincli (no contract change): have dinrep approve-registration read taskAuditor.modelRegistry() and refuse unless it equals the platform registry, and likewise in the slasher-authorization path.
    • On-chain: approveModel reverts unless IDINTaskAuditor(req.taskAuditor).modelRegistry() == address(this). That's a DINModelRegistry upgrade, and it would reject legacy pairs.

    I'd suggest a follow-up issue with the dincli check first.

  2. deploy task-auditor paired with a coordinator that has no modelId() (a legacy one-argument coordinator, or a wrong --taskCoordinator) crashes with a raw ContractLogicError instead of a BadParameter. Wrapping the modelId().call() in try/except and raising BadParameter("--taskCoordinator does not expose modelId(); …") is a three-line fix at merge.

  3. Integration harness: tests/dincli/test_02_task_contracts.py runs model-owner deploy task-coordinator/task-auditor without --model-id. It's already broken on develop, where the CLI passed one constructor argument to a two-argument contract. With this PR it fails earlier, with a clear BadParameter. It needs --model-id in the task_061026_21 Part D rework of the suite. Not this PR's job, but noting it so it isn't missed.

  4. Merge order with PR No. 261: confirmed. git merge-tree of this branch against No. 261's head conflicts in dincli/cli/modelownerd/deploy.py, since both rewrite the constructor wiring. cache_model_0/abis/ (refreshed by No. 261) also isn't touched here, so the reference model's bundled task ABIs stay stale until No. 261 lands. As the author says: No. 261 first, then rebase this one and re-run.

Not independently re-verified: the full forge test (524 passed per the PR; covered by the green CI Solidity job) and a full GI through dincli + Docker/Kubo (the integration suite is red on develop for an unrelated reason).


Every checkable claim held up, and the kill-switch tests demonstrably guard the change. The contract side is mergeable as-is. Before merge: land PR No. 261 first and rebase (the deploy.py conflict), and ideally apply observation 2. Observation 1 should become a follow-up issue rather than block this PR, since the docs already scope the guarantee to reviewed wiring. This touches foundry/src/** and implements a mechanism decision, so per MAINTAINING No. 4.2 it also needs @abrahamnash's sign-off as the second key alongside mine. The PR says "Addresses issue No. 224" (no closing keyword). That's right for now: all three decisions in No. 224 are covered here, so No. 224 can go through din-close-issue after merge, with observation 1 as its follow-up.

@umeradl

umeradl commented Oct 30, 2026

Copy link
Copy Markdown
Member

Files changed (31) — as of 2a9c546 (PR head)

Diffed against merge-base e9424af (develop). develop hasn't moved since the branch was cut. GitHub agrees: mergeable: MERGEABLE, mergeStateStatus: BLOCKED (approvals only). Confirmed with a local git merge-tree --write-tree dry run: clean. Against open PR No. 261's head it conflicts in dincli/cli/modelownerd/deploy.py (and in Developer/BACK_LOG.md, which comes from No. 261 alone). Clean against PRs No. 303 and No. 304.

foundry/src/DINTaskAuditor.sol

Field Value
Change Modified
Lines +23/-3
Diff (what exactly is in this PR) Adds ITaskModelRegistry public immutable modelRegistry. The constructor takes a fourth argument, modelRegistry_, and reverts TA_InvalidAddress if it has no code. Adds validateGIStart(uint256 gi) external view onlyTaskCoordinator. Updates the depositRewards NatSpec.
Functionality — how & why How: validateGIStart calls modelRegistry.getModelIdByTaskAuditor(address(this)) → TA_ModelNotRegistered if absent, then modelDisabled(registeredId) → TA_ModelDisabled(id), then giRewardPool[gi] == 0 → TC_GIRewardPoolNotFunded. A registry revert bubbles up (fails closed). Why: issue No. 224, decision 1 (Abraham): disabling a model must stop new GIs, and the check lives in the auditor to protect the coordinator's size headroom.
Diff vs current develop HEAD None — untouched since merge-base.
Recommended merge proposal Merged as-is. Break-then-fix: 5 of the 8 new kill-switch tests fail when the coordinator stops calling this; all 8 pass with it. Size 21,885 B.
Actual merge proposal Soon
Pending proposal Follow-up issue (not this PR): the registry wiring is self-attested. Any contract with code passes the constructor, and approveModel doesn't check modelRegistry() == address(this). Suggested first step: a dincli dinrep pre-approval check.
Local merge conflict No
GitHub merge conflict No

foundry/src/DINTaskCoordinator.sol

Field Value
Change Modified
Lines +3/-2
Diff (what exactly is in this PR) In _startGI, replaces if (dinTaskAuditorContract.giRewardPool(_GI) == 0) revert TC_GIRewardPoolNotFunded(); with dinTaskAuditorContract.validateGIStart(_GI);, plus a comment.
Functionality — how & why How: one STATICCALL after the state/index checks and before updatePassScore, GI++ and _setGIstate, shared by both startGI overloads. Why: routes the funded-pool check and the new registry checks through one auditor call, so the coordinator gains no registry wiring of its own (runtime 23,387 B, 1,189 B margin).
Diff vs current develop HEAD None — untouched since merge-base.
Recommended merge proposal Merged as-is. This line is what the break-then-fix toggled; see the verification comment.
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

foundry/src/DINShared.sol, foundry/src/DINModelRegistry.sol

Field Value
Change Modified
Lines +13/-0, +2/-1
Diff (what exactly is in this PR) DINShared: the new ITaskModelRegistry interface (two view functions), validateGIStart added to IDINTaskAuditor (as view), and the errors TA_ModelNotRegistered and TA_ModelDisabled(uint256). DINModelRegistry: disableModel NatSpec only.
Functionality — how & why How: interfaces/errors only, no storage. The registry's existing getModelIdByTaskAuditor and public modelDisabled getter are what the auditor reads. Why: lets the auditor read the registry without importing it, and corrects the disableModel doc to the new scope. No storage-layout change to the upgradeable registry.
Diff vs current develop HEAD None — untouched since merge-base.
Recommended merge proposal Merged as-is.
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

dincli/cli/modelownerd/deploy.py

Field Value
Change Modified
Lines +55/-8
Diff (what exactly is in this PR) New _task_constructor_args maps the artifact's constructor input types to arguments. It accepts coordinator (address)/(address,uint256) and auditor (address,address)/(…,uint256)/(…,uint256,address), and rejects anything else. Adds --model-id to both commands. A missing stake address is now a BadParameter (previously an UnboundLocalError). The auditor checks the paired coordinator's modelId() against --model-id before deploying, and takes the registry from din_info[network]["registry"].
Functionality — how & why How: constructor arguments are derived from the ABI, so Foundry and legacy Hardhat artifacts both deploy, and every mismatch fails before any tx. Why: develop's CLI passed one argument to the two-argument coordinator (unusable with current artifacts), and the new auditor needs the canonical registry.
Diff vs current develop HEAD None — untouched since merge-base.
Recommended merge proposal Needs a rebase after PR No. 261 (conflict in this file; both rewrite constructor wiring), then one small fix: wrap the paired modelId().call() in try/except and raise BadParameter. Today a coordinator without modelId() gives a raw ContractLogicError: execution reverted (reproduced on anvil). All other paths were verified live on anvil with the real Foundry artifacts.
Actual merge proposal Soon
Pending proposal Merge order: No. 261 first, then the author rebases and re-runs (as offered in the PR body). The try/except fix can go in that same rebase.
Local merge conflict No (against develop); Yes against PR No. 261's head
GitHub merge conflict No

dincli/abis/DINTaskAuditor.json, dincli/abis/DINTaskCoordinator.json

Field Value
Change Modified
Lines +52/-0, +0/-5
Diff (what exactly is in this PR) Regenerated: the auditor gains modelRegistry, validateGIStart, the fourth constructor input and the two new errors. The coordinator loses TC_GIRewardPoolNotFunded, which is now raised by the auditor.
Functionality — how & why How: bundled ABIs are the fallback when a manifest gives no custom task_contracts ABI. Why: they must match what forge build emits.
Diff vs current develop HEAD None — untouched since merge-base.
Recommended merge proposal Merged as-is (or regenerated after the No. 261 rebase). Sorted-JSON comparison with foundry/out: identical (160/160, 201/201 entries).
Actual merge proposal Soon
Pending proposal cache_model_0/abis/ isn't refreshed here; PR No. 261 does that. Re-check after the rebase.
Local merge conflict No
GitHub merge conflict No

tests/test_model_disable_deployment.py

Field Value
Change New
Lines +75/-0
Diff (what exactly is in this PR) Parametrised tests of _task_constructor_args (supported shapes, --model-id range, missing registry, unknown signatures), plus one command-level test of task_auditor with mocks: canonical registry passed, two txs sent, and on mismatch no constructor call and no tx.
Functionality — how & why How: no chain or wallet. Why: pins the ABI-shape mapping and the pair check.
Diff vs current develop HEAD None — file doesn't exist on develop.
Recommended merge proposal Merged as-is: 13 passed locally and green in CI Python. If the try/except fix is applied, add one case for a coordinator whose modelId() reverts.
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

foundry/test/RewardEngine.t.sol

Field Value
Change Modified
Lines +143/-3
Diff (what exactly is in this PR) The task-pair fixture now deploys against the real registry proxy, registers and approves the model (ID 0, against constructor ID 1), with optional skipRegistration / disableDuringGI flags. Adds 8 tests: both overloads disabled plus atomicity plus re-enable, private funding after disable, unregistered, registry-read failure (mockCallRevert), zero/EOA wiring, wrong registry contract, onlyTaskCoordinator, and in-flight GI completes/settles/claims but the next GI is blocked.
Functionality — how & why How: integration against the real DINModelRegistry, not a stub. The atomicity test compares the params() bytes and the GI/GIstate before and after a failed start. Why: covers every branch of validateGIStart and the "no abort" in-flight guarantee.
Diff vs current develop HEAD None — untouched since merge-base.
Recommended merge proposal Merged as-is. 8/8 pass; break-then-fix turns 5 red without the coordinator call.
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

foundry/test/utils/EnabledModelRegistry.sol + constructor updates in 12 test files

AggregatorCommitReveal, AuditorCommitReveal, DeployPlatform, DisputeResolution, EncryptedTestData, GasSimulation, LifecycleEvents, PR146SlashingRegression, ScoringValidation, SecurityFindings, StakingEnforcement, TreasuryForwarding (.t.sol)

Field Value
Change New (1) / Modified (12)
Lines +14/-0; +2/-1 each (SecurityFindings +6/-3, StakingEnforcement +3/-2)
Diff (what exactly is in this PR) A stub registry that always returns (true, 0) and not-disabled. Each existing new DINTaskAuditor(…, id) gets a fourth argument, address(new EnabledModelRegistry()).
Functionality — how & why How: keeps the isolated suites (which never register the model) startable under the new guard. Why: a mechanical update for the new constructor. The kill-switch semantics themselves are tested against the real registry in RewardEngine.
Diff vs current develop HEAD None — untouched since merge-base.
Recommended merge proposal Merged as-is. Full suite green in CI Solidity. Note that this stub is also a working example of the bypass described in observation 1 of the verification comment.
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

Documentation (9 files)

public/roles/dinrep.md, public/workflows/model-workflow.md, technical/audits/foundry-src-security-review.md, technical/contracts/{DINModelRegistry,DINShared,DINTaskAuditor,DINTaskCoordinator,DinValidatorStake}.md, technical/mechanisms/staking-mechanism.md

Field Value
Change Modified
Lines +51/-28 in total
Diff (what exactly is in this PR) Kill-switch scope (future GIs only, no abort or refund, redeploy required, the DIN-Representative reviews the wiring), the --model-id and din_info.registry deploy steps, the non-refundable registration fee (L-4 marked confirmed-intentional), S6 marked "parked for testnet", and M-2 in the audit doc marked resolved for new compatible pairs, with the original finding kept.
Functionality — how & why How: each statement matches the code above. Decisions link to issue No. 224's decision comment. Why: decisions 2 and 3 of No. 224 are docs-only, and decision 1 changes documented behaviour.
Diff vs current develop HEAD None — untouched since merge-base.
Recommended merge proposal Merged as-is. Links are green in CI Docs.
Actual merge proposal Soon
Pending proposal If the dincli wiring check from observation 1 lands later, update the "DIN-Representative must review … registry address" sentences to point at it.
Local merge conflict No
GitHub merge conflict No

Verification

CI on 2a9c546: CI OK green. The non-required Integration (full GI) job is red on the same pre-existing missing proxyAdmin assertion as develop. Locally (real via_ir profile, after npm ci): forge build --sizes, the 8 kill-switch tests (8/8), break-then-fix (5 red without the coordinator call), the ABI parity check, pytest tests/test_model_disable_deployment.py (13 passed), and a live anvil deploy of the real Foundry task pair through the PR's deploy.py commands. Full detail is in the verification comment above.

Approvals needed (MAINTAINING No. 4.2): foundry/src/** plus a mechanism decision: the peer approval (@umeradl) and @abrahamnash as the second key.

Local vs. GitHub agree: both clean against develop.

umeradl
umeradl previously approved these changes Oct 30, 2026

@umeradl umeradl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at 2a9c546 (first key; foundry/src and a mechanism decision, so @abrahamnash's approval is also required). Kill-switch tests verified by break-then-fix (5/8 fail without the coordinator call); coordinator size margin 1,189 B; ABIs match the build; deploy CLI exercised on anvil with real artifacts. Before merge: land PR No. 261 first and rebase (deploy.py conflict), and turn the raw ContractLogicError for a paired coordinator without modelId() into a BadParameter. Self-attested registry wiring goes to a follow-up issue (observation 1).

@Santiagocetran
Santiagocetran dismissed stale reviews from umeradl via dfc5bd2 October 30, 2026 13:30

Santiagocetran commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

Thanks for the detailed review and deployment checks, @umeradl!

Pushed the focused error handling in dfc5bd290644335ffd883cecec923735d45fcf16: a revert when reading the paired coordinator's modelId() now produces a clear BadParameter with guidance to check the address and compatibility. Unrelated errors still propagate.

All 24 targeted deployment tests passed in an isolated environment, covering the failure paths, absence of deployment side effects, and legacy compatibility. I haven't rerun live deployment or full-GI tests.

I'll keep this waiting for PR No. 261, then reconcile the deployment overlap and rerun the relevant checks. Abraham's requested sign-off remains outstanding, and I'll keep registry-wiring enforcement as a separate follow-up. Fresh CI and re-review of the new head are still needed.

@robertocarlous

Copy link
Copy Markdown
Collaborator

Reviewed against develop in an isolated worktree (pr-305-review on top of 90a72422, the PR head). Peer check: OK (Santiagocetran is the author; operator is not).

CI on 90a72422: CI OK ✅, Python ✅ (all non-integration tests pass), Solidity ✅, Docs ✅ — all green. Review is based on source inspection and CI.


Claimed — validateGIStart blocks both startGI overloads when the model is disabled. Verified: DINTaskCoordinator._startGI (L370–385) is the shared internal path called by both startGI(uint _GI, uint score) (L360) and startGI(uint _GI) (L366). Line 381 calls dinTaskAuditorContract.validateGIStart(_GI) unconditionally before any GI state change. Neither public overload touches state before that call.

Claimed — validateGIStart checks registry approval, disabled status, and reward pool in order. Verified by direct inspection of DINTaskAuditor.sol L446–451: getModelIdByTaskAuditor(address(this)) first; TA_ModelNotRegistered if !exists; TA_ModelDisabled(registeredId) if modelDisabled; TC_GIRewardPoolNotFunded if giRewardPool[gi] == 0. The implementation uses the registry-returned registeredId (the canonical ID from the registry), not the constructor's modelId (the stake-floor parameter). These two can differ if the registry assigns a different ID than the one passed to the constructor; the PR uses the authoritative registry value, which is correct.

Claimed — ITaskModelRegistry interface matches the real DINModelRegistry functions. Verified against DINModelRegistry.sol: getModelIdByTaskAuditor(address) is at L422–429, returns (bool exists, uint256 modelId) using a val - 1 sentinel to distinguish model ID 0 from "not registered" — exactly what the interface declares. modelDisabled(uint256) is the auto-getter for the public modelDisabled mapping at L119. Both signatures match the interface in DINShared.sol.

Claimed — modelRegistry is immutable and validated as a contract at construction. Verified: declared ITaskModelRegistry public immutable modelRegistry at L22; constructor checks modelRegistry_.code.length == 0 (L419) and reverts TA_InvalidAddress before assignment (L428). No setter exists; cannot be reassigned after deployment.

Claimed — all three errors are file-level selectors, not contract-scoped. Verified: TC_GIRewardPoolNotFunded (L359), TA_ModelNotRegistered (L488), and TA_ModelDisabled(uint256) (L490) are all in DINShared.sol. The pre-PR coordinator used TC_GIRewardPoolNotFunded as a file-level error; the selector is unchanged.

Claimed — 8 new kill-switch tests in RewardEngine.t.sol. Verified by CI (Solidity ✅) and by inspection: 8 new tests including test_disabledModel_blocksBothOverloadsAtomically_andReenable (both startGI overloads blocked, state unchanged, re-enable resumes) and test_disabledInFlight_completesSettlesClaims_butCannotStartNext (in-flight GI completes normally; next GI blocked after disable). EnabledModelRegistry.sol test stub always returns (true, 0) / false and is used in all non-kill-switch tests — this keeps existing test suites unaffected by the new auditor constructor arg.

Claimed — SecurityFindings.t.sol constructors updated. Confirmed: the existing security-finding regression tests now pass the 4-arg auditor constructor (CI Solidity ✅).

Claimed — dincli model-owner deploy handles the new 4-arg auditor constructor. Verified: _task_constructor_args in dincli/cli/modelownerd/deploy.py dispatches on constructor arity; 4-arg auditor passes [stake, coordinator, model_id, registry]. test_supported_constructor_shapes in test_model_disable_deployment.py exercises all five supported shapes including ("address", "address", "uint256", "address") → ["stake", "coordinator", 0, "registry"]; Python CI green.

Claimed — DINTaskAuditor.json ABI updated. Verified: new constructor input modelRegistry_, modelRegistry view function, validateGIStart function, TA_ModelNotRegistered and TA_ModelDisabled errors all present.

Claimed — security review M-2 updated with resolution. Verified in foundry-src-security-review.md: "Resolution" paragraph added above the original finding text; L-4 updated to "Confirmed intentional — [#224, Oct 26 decision] / BL-35." The original finding text is retained as audit context with the label "Original finding — ... superseded for new compatible pairs."

Claimed — full-gi.yml extracted from ci.yml. Verified: 178 lines removed from ci.yml (the integration-paths + integration jobs); equivalent job added as full-gi.yml with on: workflow_dispatch (manual only). The ci-ok job in ci.yml never waited on integration, so this is a no-op for required checks.


Finding — P2: Developer/BACK_LOG.md BL-34 status not updated to resolved. The PR changes BL-34 from "🆕 New — unaddressed" to "🆕 Decided, unimplemented — Abraham (Oct 26, [No. 224]): make the kill-switch real…" — but this PR IS that implementation. BL-34 should be "✅ Resolved — validateGIStart wired in DINTaskAuditor.sol; both startGI overloads check registry approval and disabled status before any state change. Compatible task pairs required; existing deployed pairs unaffected. PR No. 305."

Merge state: branch is BEHIND develop (GitHub mergeStateStatus: BEHIND), no conflict (local merge-tree exit 0). Needs update-branch before merge. foundry/src/** touched — a second sign-off from @umeradl or @abrahamnash is required per MAINTAINING No. 4.2 before this merges.

@robertocarlous

Copy link
Copy Markdown
Collaborator

Head at review: 90a72422
Merge base (upstream develop at PR creation): earlier develop tip
Current upstream develop: behind but clean — no conflict (merge-tree exit 0)


foundry/src/DINShared.sol

Key Value
Change +12 lines: ITaskModelRegistry interface (getModelIdByTaskAuditor, modelDisabled); IDINTaskAuditor.validateGIStart declaration; TA_ModelNotRegistered and TA_ModelDisabled(uint256) errors
Pending proposal None — interface matches real DINModelRegistry functions; errors declared at file level; selectors correct
Recommended merge proposal Merge as-is
Actual merge proposal Soon

foundry/src/DINTaskAuditor.sol

Key Value
Change +26 lines: ITaskModelRegistry public immutable modelRegistry; constructor gains 4th arg modelRegistry_ (non-EOA check); validateGIStart(uint256 gi) external view onlyTaskCoordinator checks registry approval + disabled + reward pool in order
Pending proposal None — implementation correct; uses registry-returned ID (not constructor modelId); all errors from DINShared.sol
Recommended merge proposal Merge as-is
Actual merge proposal Soon

foundry/src/DINTaskCoordinator.sol

Key Value
Change -2/+2 lines: _startGI replaces direct giRewardPool check with dinTaskAuditorContract.validateGIStart(_GI)
Pending proposal None — both public overloads call _startGI; call is unconditional before any state mutation
Recommended merge proposal Merge as-is
Actual merge proposal Soon

foundry/test/utils/EnabledModelRegistry.sol (new)

Key Value
Change +19 lines: test stub always returns (true, 0) / false — satisfies new 4-arg auditor constructor in all non-kill-switch tests
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

foundry/test/RewardEngine.t.sol

Key Value
Change +130 lines: 8 new kill-switch tests; covers both startGI overloads blocked atomically, state unchanged, re-enable resumes; in-flight GI completes normally after disable but next GI blocked
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

foundry/test/SecurityFindings.t.sol

Key Value
Change Constructor updates to pass the 4-arg auditor form
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

dincli/cli/modelownerd/deploy.py

Key Value
Change _task_constructor_args helper added; handles 2/3-arg coordinator and 2/3/4-arg auditor constructors; --model-id option; reads registry from din_info; cross-checks --model-id against modelId() on the paired coordinator
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

dincli/abis/DINTaskAuditor.json

Key Value
Change modelRegistry_ added to constructor inputs; modelRegistry view function; validateGIStart function; TA_ModelNotRegistered and TA_ModelDisabled errors
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

tests/test_model_disable_deployment.py (new)

Key Value
Change +184 lines: constructor-shape tests, model-ID validation, registry requirement, mismatch rejection, full deploy flow with/without registry arg
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

Developer/BACK_LOG.md

Key Value
Change BL-24 no additional change; BL-27 status clarified (more detail on PR No. 261 scope); BL-34 updated from "New — unaddressed" to "Decided, unimplemented — Abraham (Oct 26, No. 224)"; BL-35 updated from "New — unaddressed" to "Decided — keep as intended (Abraham, Oct 26)"; BL-28 status updated with more detail on planned command
Pending proposal P2 BL-34 status should be "✅ Resolved" — this PR implements the decision. Replace the current BL-34 status with: ✅ Resolved — \validateGIStart` wired in `DINTaskAuditor.sol`; both `startGI` overloads check registry approval and disabled status via `_startGI`. Compatible task pairs required; existing deployed pairs unaffected. PR No. 305`
Recommended merge proposal Update BL-34 to "✅ Resolved", then merge
Actual merge proposal Soon

Documentation/technical/audits/foundry-src-security-review.md

Key Value
Change M-2 resolution paragraph added; L-4 confirmed intentional (BL-35); original finding text retained as audit context
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

Documentation/technical/contracts/DINTaskAuditor.md

Key Value
Change modelRegistry (immutable) added to dependencies table; validateGIStart added to coordinator-only caller table; new "Future-GI guard and deployment" subsection covering constructor, guard semantics, disabling/reenabling, non-upgradeable caveat
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

Documentation/technical/contracts/DINTaskCoordinator.md, DINShared.md, DINModelRegistry.md, DinValidatorStake.md

Key Value
Change Cross-references updated to mention validateGIStart call in _startGI; DINModelRegistry.md notes getModelIdByTaskAuditor is called by compatible task auditors
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

.github/workflows/ci.yml / .github/workflows/full-gi.yml (new)

Key Value
Change integration-paths + integration jobs removed from ci.yml (-178 lines); equivalent integration job added to new full-gi.yml as workflow_dispatch only (manual trigger)
Pending proposal None — ci-ok never depended on the integration job; this is a non-breaking extraction
Recommended merge proposal Merge as-is
Actual merge proposal Soon

Remaining documentation files (skills, MAINTAINING.md, CONTRIBUTING.md, ROADMAP.md, role/workflow docs, README)

Key Value
Change Cross-references, task-contract deployment steps, and skill descriptions updated to reflect the 4-arg auditor constructor and validateGIStart behavior
Pending proposal None
Recommended merge proposal Merge as-is
Actual merge proposal Soon

@robertocarlous robertocarlous left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2-1: Developer/BACK_LOG.md BL-34 status should be updated to ✅ Resolved — this PR implements the fix. Please change the current "Decided, unimplemented" text to: "✅ Resolved — validateGIStart wired in DINTaskAuditor.sol; both startGI overloads check registry approval and disabled status via _startGI. Compatible task pairs required; existing deployed pairs unaffected. PR No. 305." Verified at 90a72422. Everything else — contracts, tests, CLI, ABIs, docs — is correct and merge-ready once this row is updated.

Copy link
Copy Markdown
Collaborator Author

Thanks for the reviews and thorough checks!

Fixed BL-34 in 6970f5d to mark it as resolved and describe the implemented guard, including the compatibility caveat for existing deployments.

Only the backlog status changed. Diff checks and all 293 documentation-link checks passed.

I’ll reconcile the deployment changes once PR No. 261 lands, then rerun the relevant checks and request a fresh review.

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough review! I’ve reconciled this branch with develop after PR No. 261 and pushed the updates through e8e7349.

The CLI now checks the coordinator’s protocol version before deploying or pairing an auditor, rejecting incompatible combinations while preserving matching current and legacy pairs. The artifacts and docs are updated too.

Local validation is green: 735 Python tests and 527 Foundry tests passed. Live CLI checks confirmed that all eight incompatible pairings reject without transactions or address writes. Astra also rechecked the fix and found no new issues.

Fresh CI is running. Persistent IPFS publication and independent retrieval, along with fresh CI results and the required reviews, are still pending before this is ready to merge. The current IPFS evidence covers local offline pinning and retrieval only.

@Santiagocetran

Copy link
Copy Markdown
Collaborator Author

Thanks again for the thorough reviews! PR #261 is now incorporated, the deployment changes are reconciled, and the earlier requested fixes are addressed. Latest CI is green: 527 Foundry tests and 895 Python tests passed, including artifact parity and contract-size checks.
@umeradl @robertocarlous

@Santiagocetran

Copy link
Copy Markdown
Collaborator Author

Reconciled PR #305 with the latest develop, resolved the ABI conflict, and regenerated the task artifacts and manifest CIDs. Pushed in 2d9f5c9. GitHub now reports the branch as mergeable.

Fresh validation passed:

  • 534 Foundry tests
  • 895 Python tests
  • 32 Hardhat tests
  • Artifact parity, contract-size gate, and all 298 documentation links
  • 27 live checks on a separate device, covering real CLI deployment/pairing, incompatible-pair rejection, and both GI-start overloads blocking unregistered or disabled models and succeeding after re-enabling

Fresh GitHub CI is queued. Passing CI and the required peer review/sign-offs remain before merge.

@umeradl

umeradl commented Nov 7, 2026

Copy link
Copy Markdown
Member

Re-review of the fix commits: 2a9c546 (reviewed 2026-10-30) → f46dd02 (current HEAD). These are the 6 non-merge commits since then (dfc5bd2, 6970f5d, 33d5f83, e8e7349, 7d20067, c8a7224) plus the develop reconcile merges. Reviewed in the same isolated worktree, hard-reset to f46dd02. develop's tip ad94870 is also the merge-base, so the branch is fully up to date. git merge-tree --write-tree against origin/develop is clean (exit 0), and GitHub agrees: mergeable: MERGEABLE, mergeStateStatus: BLOCKED (approvals only). Peer check: OK.

CI on f46dd02: CI OK green: Solidity (full forge test, size gate, and the new artifact-bundle gate), Python and Docs. Reproduced locally (CI doesn't cover these): the deploy CLI's new generation check on a live anvil, bytecode reproducibility of the bundled artifacts, contract sizes, and the manifest CIDs.

What changed in the PR's own diff since 2a9c546: 23 of the 42 files are byte-for-byte the same change I reviewed (this includes DINTaskAuditor.sol, DINShared.sol and DINModelRegistry.sol). The contract delta is a single pure getter, DINTaskCoordinator.taskProtocolVersion() == 1. Everything else is in deploy.py, the bundled artifacts, CI, tests and docs.

Claimed: a revert reading the paired coordinator's modelId() now gives a clear BadParameter (dfc5bd2, my observation No. 2).

Verified — fixed: ContractLogicError/BadFunctionCallOutput are wrapped; ConnectionError/ValueError still propagate (test_auditor_command_preserves_unrelated_read_errors). On anvil, an EOA passed as --taskCoordinator gives BadParameter: Cannot read modelId() from the paired coordinator; verify the address… and sends 0 tx.

Claimed: the CLI rejects incompatible coordinator/auditor generations before the one-shot pairing (e8e7349).

Verified — on a live anvil, with real artifacts: I drove dincli's own deploy.task_coordinator and deploy.task_auditor; only din_info, .env writes and sleep were stubbed. "Develop" below means develop's bundled cache_model_0/abis artifacts, i.e. the generation before this PR:

Pair Coordinator taskProtocolVersion() Result
PR coordinator + PR 4-arg auditor 1 deployed and paired (3 tx); dinTaskAuditorContract() = new auditor
PR coordinator + develop 3-arg auditor 1 BadParameter: Incompatible task contract generations: coordinator … 1, auditor … 0 (0 tx)
develop coordinator + PR 4-arg auditor absent BadParameter: … coordinator … 0, auditor … 1 (0 tx)
develop coordinator + develop 3-arg auditor absent deployed and paired (3 tx)

Both rejected rows matter. Row 2 would brick the pair: the new coordinator calls validateGIStart, which the old auditor doesn't have, so every startGI would revert. Row 3 would silently drop the kill switch: the old coordinator never calls the guard. test_incompatible_generations_send_no_transaction and test_missing_version_getter_only_allows_legacy_pairs cover the same matrix with mocks.

Claimed: BL-34 is marked resolved (6970f5d, Robbert's P2).

Verified: the BL-34 status cell is now Robbert's suggested text, verbatim. BL-31 and BL-35 also record what this PR documents.

Claimed: PR No. 261 is reconciled, and the reference-model artifacts and manifest CIDs are refreshed (my observation No. 4).

Verified — exact: after a local forge build (real via_ir profile), python3 .github/scripts/check_task_artifacts.py prints "Task ABI bundles and reference creation bytecode match the Foundry build". The committed creation bytecode reproduces byte-for-byte on a second machine, not just on CI. I recomputed both manifest CIDs as raw CIDv1 (sha2-256) of the committed files and both match: bafkreigjv7cx… for DINTaskCoordinator.json (103,902 B) and bafkreidgp65w… for DINTaskAuditor.json (102,120 B). I could not confirm they're pinned: public gateways rate-limited or timed out.

Claimed: the two pinned remappings make the bytecode reproducible (c8a7224).

Verified, indirectly: with the pins in place, my local build matches the committed bytecode exactly, as above. Neither added remapping (erc4626-tests/, halmos-cheatcodes/) is imported by foundry/src. They only pin what forge's auto-detection would otherwise choose between duplicate nested libs, and that choice shows up in the metadata hash.

Claimed: contract-size gate passes.

Verified — locally: DINTaskCoordinator runtime is 23,321 B (1,255 B margin, warn tier, above the 1,024 B fail line). It was 23,387 B at 2a9c546: develop shrank it in the meantime, and the new getter costs very little. DINTaskAuditor is 21,887 B (2,689 B margin).

Claimed: the kill-switch tests still guard the change, plus a new disabled-status read-failure test.

Verified: forge test --match-contract RewardEngine: 40 passed locally. The new test_disabledStatusReadFailure_failsClosed covers a registry whose modelDisabled reverts. test_registryWiring_rejectsZeroAndEOA also asserts taskProtocolVersion() == 1. The break-then-fix from my first review still applies, because the coordinator's validateGIStart line is unchanged.

Previous observations — status:

  1. Self-attested registry wiring — still true by design (the auditor's constructor only checks modelRegistry_.code.length), and no follow-up issue exists yet. Issue No. 185 (DIN-Rep approval automation) is the natural home, as two checks before approveModel/add-slasher: taskAuditor.modelRegistry() == <platform registry> and taskCoordinator.taskProtocolVersion() == 1. I can add that there.
  2. Raw ContractLogicError — fixed, see above.
  3. Integration test needs --model-id — moot now: since PR No. 261 the coordinator defaults to totalModels() and the auditor to the coordinator's modelId(). Full GI is manual-only and I didn't run it.
  4. Merge order with No. 261 — done. No. 261 landed and was reconciled, and cache_model_0 ABIs are now refreshed and gated.

New observations (none blocking):

  1. The new bytecode-parity gate is a standing cost for every future task-contract PR. check_task_artifacts.py requires cache_model_0/abis/*.json creation bytecode to equal the build exactly, and it runs in the required Solidity job. Because the metadata hash is part of the bytecode, any edit to DINTaskCoordinator.sol, DINTaskAuditor.sol, DINShared.sol or an OZ submodule now fails CI until both bundles are regenerated, even a NatSpec-only edit. The two manifest CIDs then have to be updated and re-pinned too. Two task-contract PRs open at the same time will always conflict in these three files. Two small changes would make that manageable:

    • (a) The failure message should name the regen command. I checked that dincli system dump-abi --artifact foundry/out/<C>.sol/<C>.json --output cache_model_0/abis --bytecode reproduces both committed files byte-for-byte.
    • (b) The gate should also check the manifest's two ipfs CIDs against the files. Right now a refreshed bundle with a stale CID passes, which is exactly the drift this gate is meant to stop. The raw-CIDv1 computation is a few lines of stdlib (0x01 0x55 0x12 0x20 + sha256, base32).

    Whether cache_model_0 should keep exact-bytecode parity at all, rather than ABI-only, is a maintainer call. I'm flagging it rather than asking for it here.

  2. Unreachable "no deployed code" message: in deploy task-auditor, the w3.eth.get_code check runs after the modelId() read. So for the 3/4-arg auditors, an address without code always hits the modelId() message instead (the anvil EOA row above). Moving the get_code check above the modelId() read gives the clearer error. Two-line move.

  3. Please confirm the two new bafkrei… CIDs are pinned on the project's IPFS provider. Nothing in CI fetches them.

Not independently re-verified: the full forge test (527 per the author; covered by the green Solidity job), the full non-integration pytest (covered by the green Python job), and a full GI through dincli + Docker/Kubo (full-gi.yml is manual-only).


Every new claim held up. The contract change since my approval is one pure getter, and the CLI generation check behaves correctly on a live chain in all four pairings. Mergeable as-is. New observations 1(a), 2 and 3 are small and can go in before merge or at merge; 1(b) and the exact-bytecode question can be a follow-up. My earlier approval was dismissed by the new commits; a fresh first-key approval follows separately. This touches foundry/src/** and implements the No. 224 mechanism decision, so it still needs @abrahamnash's approval as the second key (MAINTAINING No. 4.2). @robertocarlous, your P2 is applied verbatim at 6970f5d. Could you re-review or dismiss your change request? The PR body has no closing keyword. That's still right: No. 224 goes through din-close-issue after merge, with previous observation 1 as its follow-up.

@umeradl

umeradl commented Nov 7, 2026

Copy link
Copy Markdown
Member

Files changed (42) — as of f46dd02 (PR head), re-review delta vs 2a9c546

Diffed against merge-base ad94870, which is the current develop tip, so the branch is fully up to date. GitHub agrees: mergeable: MERGEABLE, mergeStateStatus: BLOCKED (approvals only). A local git merge-tree --write-tree dry run is clean.

23 files are unchanged since my first review: the PR's own change to each is identical to 2a9c546, so their rows in the first merge-proposal comment stand. They are foundry/src/DINTaskAuditor.sol, DINShared.sol, DINModelRegistry.sol, foundry/test/utils/EnabledModelRegistry.sol, the 12 constructor-arg test updates (AggregatorCommitReveal, AuditorCommitReveal, DeployPlatform, DisputeResolution, EncryptedTestData, GasSimulation, LifecycleEvents, PR146SlashingRegression, ScoringValidation, SecurityFindings, StakingEnforcement, TreasuryForwarding), dincli/abis/DINTaskAuditor.json, and the docs DINModelRegistry.md, DINShared.md, DinValidatorStake.md, staking-mechanism.md, dinrep.md and foundry-src-security-review.md. The 19 files that changed or are new follow.

foundry/src/DINTaskCoordinator.sol

Field Value
Change Modified (changed since 2a9c546)
Lines +10/-2
Diff (what exactly is in this PR) The _startGI swap to dinTaskAuditorContract.validateGIStart(_GI) is unchanged. New: function taskProtocolVersion() external pure returns (uint256) { return 1; }.
Functionality — how & why How: a constant getter with no storage, so there is no layout impact (the task contracts are non-upgradeable anyway). Why: deploy tooling needs to tell a guard-calling coordinator from a legacy one before the irreversible setDINTaskAuditorContract. Without it, a mismatched pair either bricks startGI or silently drops the kill switch.
Diff vs current develop HEAD None (develop is the merge-base)
Recommended merge proposal Merge as-is. 23,321 B runtime, 1,255 B margin (local contract_size_gate.py).
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

dincli/cli/modelownerd/deploy.py

Field Value
Change Modified (changed since 2a9c546)
Lines +139/-53
Diff (what exactly is in this PR) Adds _task_constructor_signature (an allow-list of 2 coordinator and 3 auditor shapes) and _task_constructor_args. task-coordinator sets the DinToken and defaults --model-id only for the 2-arg coordinator. task-auditor wraps the modelId() read in a BadParameter, checks get_code, reads taskProtocolVersion() (absent → 0), rejects mismatched generations, and keeps the two legacy families apart. Drops the unused get_env_key import.
Functionality — how & why How: all checks run before the first tx. The auditor's generation is inferred from its constructor arity (4 args → 1), and the coordinator must report the same value. Why: auditor pairing is one-shot. Verified on a live anvil in all four pairings (see the verification comment).
Diff vs current develop HEAD None
Recommended merge proposal Merge, ideally with new observation No. 2 first: move the w3.eth.get_code(task_coordinator_address) check above the modelId() read so an address without code gets the "no deployed code" message. Cosmetic: the import reshuffle dropped the blank line between typing and typer.
Actual merge proposal Soon
Pending proposal New observation No. 2 (non-blocking)
Local merge conflict No
GitHub merge conflict No

.github/scripts/check_task_artifacts.py (new), .github/workflows/ci.yml, tests/test_task_artifact_check.py (new)

Field Value
Change New script plus its tests; a 3-line CI step after forge build in the Solidity job
Lines +101 / +3 / +133
Diff (what exactly is in this PR) Compares the canonical (order-insensitive) ABI of dincli/abis and cache_model_0/abis against foundry/out. Also requires cache_model_0 creation bytecode to equal the build exactly, and on mismatch prints diagnostics (first differing byte, lengths, sha256, compiler settings, source hashes).
Functionality — how & why How: stdlib only; exits 1 with one line per drift. Why: this is the gate that stops the reference-model bundles going stale again (BL-25-style drift). Verified locally: passes on the committed files after a real via_ir build.
Diff vs current develop HEAD None
Recommended merge proposal Merge. Before merge or at merge: make the bytecode-mismatch message name the regen command, dincli system dump-abi --artifact foundry/out/<C>.sol/<C>.json --output cache_model_0/abis --bytecode, which I verified reproduces both committed files byte-for-byte (new observation No. 1(a)).
Actual merge proposal Soon
Pending proposal New observation No. 1(b), a manifest-CID check in the gate, which can be a follow-up. Whether cache_model_0 keeps exact-bytecode parity at all is a maintainer call.
Local merge conflict No
GitHub merge conflict No

cache_model_0/abis/DINTaskCoordinator.json, cache_model_0/abis/DINTaskAuditor.json, cache_model_0/manifest.json

Field Value
Change Modified (new to this PR since 2a9c546)
Lines +20/-7, +58/-6, +3/-3
Diff (what exactly is in this PR) The ABI and creation bytecode are regenerated from the PR's build. The manifest's two ipfs CIDs move from bafybei… to raw CIDv1 bafkreigjv7cx… / bafkreidgp65w…, and a trailing newline is added.
Functionality — how & why How: the reference model now deploys the guarded pair. Why: without this, model 0's bundled artifacts would deploy a coordinator that the auditor bundle can't pair with.
Diff vs current develop HEAD None
Recommended merge proposal Merge as-is. Bytecode is byte-identical to a local build; both CIDs recompute exactly from the committed files.
Actual merge proposal Soon
Pending proposal Author to confirm both CIDs are pinned (new observation No. 3); public gateways timed out for me
Local merge conflict No
GitHub merge conflict No

dincli/abis/DINTaskCoordinator.json

Field Value
Change Modified (changed since 2a9c546)
Lines +13/-5
Diff (what exactly is in this PR) Adds taskProtocolVersion (pure). TC_GIRewardPoolNotFunded still leaves the coordinator ABI, as before.
Functionality — how & why ABI parity with the build, now enforced by check_task_artifacts.py
Diff vs current develop HEAD None
Recommended merge proposal Merge as-is (gate passes locally)
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

foundry/remappings.txt

Field Value
Change Modified (new to this PR since 2a9c546)
Lines +2/-0
Diff (what exactly is in this PR) Pins erc4626-tests/ and halmos-cheatcodes/ to the copies nested under openzeppelin-contracts-upgradeable/lib/
Functionality — how & why How: neither is imported by foundry/src. The pins only stop forge's auto-remapping choosing between duplicate nested libs per environment. Why: that choice changes the metadata hash, and therefore the bytecode the new gate compares exactly.
Diff vs current develop HEAD None
Recommended merge proposal Merge as-is. My local build reproduces CI's bytecode with these pins.
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

foundry/test/RewardEngine.t.sol

Field Value
Change Modified (changed since 2a9c546)
Lines +161/-3
Diff (what exactly is in this PR) The 8 kill-switch tests are unchanged. New: test_disabledStatusReadFailure_failsClosed, and a taskProtocolVersion() == 1 assertion in test_registryWiring_rejectsZeroAndEOA.
Functionality — how & why Covers a registry whose modelDisabled read reverts: startGI must fail closed, not open
Diff vs current develop HEAD None
Recommended merge proposal Merge as-is; 40/40 pass locally
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

tests/test_model_disable_deployment.py, tests/test_deploy_task_contracts.py, tests/test_task_auditor_model_id_guard.py

Field Value
Change Modified (the first changed since 2a9c546; the other two new to this PR since then)
Lines +376/-0, +29/-4, +22/-16
Diff (what exactly is in this PR) The supported/unknown constructor shapes, invalid IDs, registry requirement, modelId() revert vs unrelated errors, the generation-mismatch matrix and the missing-version-getter paths. Each asserts no constructor/tx/.env/pairing side effect on rejection. The existing No. 261 tests are adapted to the new shapes.
Functionality — how & why Mocked unit coverage of the same matrix I ran live on anvil
Diff vs current develop HEAD None
Recommended merge proposal Merge as-is (green in CI Python)
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

Developer/BACK_LOG.md

Field Value
Change Modified
Lines +3/-3
Diff (what exactly is in this PR) BL-34 is now Robbert's suggested "✅ Resolved" text, verbatim. BL-31 gets a parked-behaviour note and BL-35 a "documented by PR No. 305" note.
Functionality — how & why Closes Robbert's P2
Diff vs current develop HEAD None
Recommended merge proposal Merge as-is
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

Documentation/public/workflows/model-workflow.md, Documentation/public/roles/model-owner.md, Documentation/technical/contracts/DINTaskAuditor.md, Documentation/technical/contracts/DINTaskCoordinator.md

Field Value
Change Modified (2 changed since 2a9c546, 2 new to this PR since then)
Lines +12/-3, +3/-3, +13/-2, +6/-3
Diff (what exactly is in this PR) Deploy examples now point at foundry/out artifacts with --model-id. Adds the taskProtocolVersion() and legacy-pair rules, the registry requirement in din_info.json, and a note that the cache-default-artifacts bootstrap CIDs are historical.
Functionality — how & why Documents exactly what deploy.py enforces; matches the code as read
Diff vs current develop HEAD None
Recommended merge proposal Merge as-is (CI Docs green). The model-workflow.md paragraph is dense; splitting it into a short list would read better, but that's optional.
Actual merge proposal Soon
Pending proposal None
Local merge conflict No
GitHub merge conflict No

Verification

See the re-review verification comment above: live-anvil pairing matrix (4 pairings plus the EOA case), local via_ir build with the artifact gate passing, RewardEngine 40/40, the size gate, and the manifest CIDs recomputed. Everything else is from CI run 42274091689 on f46dd02.

Local vs. GitHub agree: yes, clean merge in both, branch up to date with develop.

@umeradl umeradl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at f46dd02 (first key; foundry/src and the No. 224 mechanism decision, so @abrahamnash's approval is also required). Since 2a9c546 the only contract change is the pure taskProtocolVersion() getter; coordinator 23,321 B (1,255 B margin). Deploy CLI generation check verified on live anvil across all four pairings plus a no-code address; bundled cache_model_0 bytecode reproduces byte-for-byte locally and both manifest CIDs recompute exactly. Non-blocking: regen command in the artifact-gate message, get_code check before the modelId() read, confirm the new CIDs are pinned.

@robertocarlous robertocarlous left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at f46dd02: validateGIStart kill-switch (BL-34), taskProtocolVersion generation guard, deploy.py protocol-version check, forge test 535/535 passed, CI all green. Umer's approval at f46dd02 provides the foundry/src second key.

@robertocarlous
robertocarlous added this pull request to the merge queue Nov 7, 2026
Merged via the queue into InfiniteZeroFoundation:develop with commit 3def72f Nov 7, 2026
4 checks passed
@umeradl

umeradl commented Nov 7, 2026

Copy link
Copy Markdown
Member

Actual outcome — PR #305 merged as-is (pushed)

This supersedes the pre-merge review comment above with what actually happened.

One commit on origin/develop:

  1. 3def72f — real merge of this PR (merge commit, not squash) through the merge queue. Its parents are ad94870 (develop) and f46dd02 (the PR head, the SHA I re-reviewed and approved). There were no conflicts, as predicted. GitHub agrees: PR shows MERGED, merged by @robertocarlous at 14:43 UTC.

No deviation commit was needed.

Sign-offs: @robertocarlous as peer (approved at f46dd02, 14:36 UTC, which also cleared his Oct 31 change request), and @umeradl as the foundry/src/** second key and mechanism sign-off (approved at f46dd02, 12:17 UTC). That's what MAINTAINING No. 4.2 requires for a PR not authored by Umer. My re-review comment also asked for @abrahamnash as second key, which went beyond what No. 4.2 requires. The mechanism sign-off section says a PR doesn't wait for Abraham on GitHub, so his approval wasn't needed here.

Files unchanged from the PR (42 of 42)

git diff f46dd02 3def72f over the PR's 42 files is empty, so every file landed exactly as authored. None of them has been touched on develop since.

  • Contracts: foundry/src/DINTaskCoordinator.sol, DINTaskAuditor.sol, DINShared.sol, DINModelRegistry.sol, foundry/remappings.txt
  • Contract tests: 14 files under foundry/test/ (including the new utils/EnabledModelRegistry.sol)
  • Artifacts: cache_model_0/abis/DINTaskCoordinator.json, DINTaskAuditor.json, cache_model_0/manifest.json, dincli/abis/DINTaskCoordinator.json, DINTaskAuditor.json
  • CLI and CI: dincli/cli/modelownerd/deploy.py, .github/scripts/check_task_artifacts.py (new), .github/workflows/ci.yml
  • Python tests: tests/test_model_disable_deployment.py (new), test_task_artifact_check.py (new), test_deploy_task_contracts.py, test_task_auditor_model_id_guard.py
  • Docs: Developer/BACK_LOG.md, Documentation/public/roles/{dinrep,model-owner}.md, Documentation/public/workflows/model-workflow.md, five Documentation/technical/contracts/*.md files, mechanisms/staking-mechanism.md, audits/foundry-src-security-review.md

Not done before merge (follow-ups)

The non-blocking observations from the re-review weren't applied before merge (the head stayed at f46dd02), so they're still open:

  • No. 1(a): check_task_artifacts.py's bytecode-mismatch message should name the regeneration command (dincli system dump-abi --artifact foundry/out/<C>.sol/<C>.json --output cache_model_0/abis --bytecode).
  • No. 1(b): the gate should also check cache_model_0/manifest.json's two ipfs CIDs against the files. Whether cache_model_0 should keep exact-bytecode parity at all is a maintainer call.
  • No. 2: in deploy task-auditor, move the w3.eth.get_code check above the modelId() read so that an address with no code gets the "no deployed code" message.
  • No. 3: confirm the two new bafkrei… CIDs are pinned on the project's IPFS provider.

Issue No. 224 goes through din-close-issue next, with these as its follow-up list.

Verification

The merge-queue run on the merge-group commit and the post-merge push run on 3def72f are both green: Python, Solidity (including the new task-artifact gate), Docs and CI OK. CodeQL (Code Quality) also passed. The full GI through dincli + Docker/Kubo wasn't run, because full-gi.yml is manual-only.

Local vs. GitHub agree: clean merge, as both predicted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants